/* jpart.js nodejs常用工具集合 by zhaoeast@126.com 2018-8-30 */

const dayMillis = 24*3600*1000;	// 每天的毫秒数

// 下划线转驼峰命名法
function _toCamelCase(name){
	var p = 0, p0 = 0;
	var buffer = [];
	while (p < name.length){
		var code = name.charCodeAt(p);
		if (code == 95){
			// 如果出现2个连续的下划线
			if (p > p0 + 1){
				if (p0 != 0){
					buffer.push(name.charAt(p0).toUpperCase());
					buffer.push(name.substring(p0 + 1, p));
				} else {
					buffer.push(name.substring(p0, p));
				}
			}
			p0 = p + 1;
		}
		p++;
	}
	if (p0 == 0)
		return name;
	// 添加最后一段
	buffer.push(name.charAt(p0).toUpperCase());
	buffer.push(name.substring(p0 + 1));
	return buffer.join('');
}

// 驼峰转下划线命名风格
function _toUnderScoreCase(name){
	var p = 0, p0 = 0;
	var buffer = [];
	while (p < name.length){
		var code = name.charCodeAt(p);
		if (code >= 65 && code <= 90){
			buffer.push(name.substring(p0, p));
			buffer.push('_');
			buffer.push(name.charAt(p).toLowerCase());
			p0 = p + 1;
		}
		p++;
	}
	if (p0 == 0)
		return name;
	buffer.push(name.substring(p0));
	return buffer.join('');
}

// 验证一个节点，无效返回 true
function validate(bean, name, rule) {
	var valid = true;
	var message = '';
	var value = bean[name];
	if (value){
		// 只检验字符串
		if (typeof value != 'string')
			return [valid, message];
		if (typeof rule == 'string'){
			if (rule.charAt(0) == '/'){
				// 正则表达式
				var regex = new RegExp(rule.substring(1, rule.length - 1));
				valid = regex.test(value);
				if (!valid)
					message = '该' + name + ' 无效';
			} else if (rule == 'int'){
				// 整数格式
				var reInteger = /^[0-9]*$/;
				valid = reInteger.test(value);
				if (!valid)
					message = '该 ' + name + ' 必须为整数';
				//else
				//	bean[name] = parseInt(value);	// 转化为整形
			} else if (rule == 'number'){
				// 数字格式
				var reNumber = /^[0-9\.\-]*$/;
				valid = reNumber.test(value);
				if (!valid)
					message = '该 ' + name + ' 必须是一个数字';
			} else if (rule == 'date'){
				// 日期格式
				var reDate = /^\d{4}-[01]\d-[0123]\d/;
				valid = reDate.test(value);
				if (!valid)
					message = '该 ' + name + ' 必须是日期格式';
			} else if (rule == 'datetime'){
				// 日期或时间格式
				var reDate = /^\d{4}-[01]\d-[0123]\d([\sT][012]\d:[0-5]\d:[0-5]\d)?(\.\d{3}\w)?$/;
				valid = reDate.test(value);
				if (!valid)
					message = '该 ' + name + ' 必须是日期时间格式';
			} else if (rule == 'json'){
				// json格式
				try {
					JSON.parse(value);
				} catch (e) {
					valid = false;
					message = '该 ' + name + ' 必须是json格式';
				}
			} else {
				// 验证字符串长度
				var p = rule.indexOf(':');
				var param = '';
				if (p != -1){
					param = rule.substring(p + 1);
					rule = rule.substring(0, p);
				}
				var size = param ? parseInt(param) : 0;
				if (rule == 'max'){
					valid = value.length <= size;
					if (!valid)
						message = '该 ' + name + '  长度不能大于' + size + ' 个字符';
				} else if (rule == 'min'){
					valid = value.length >= size;
					if (!valid)
						message = '该 ' + name + ' 长度不能小于 ' + size + ' 个字符';
				} else if (rule == 'length'){
					valid = value.length == size;
					if (!valid)
						message = '该 ' + name + ' 不能等于 ' + size + ' 个字符';
				}
			}
		} else {
			// 按正则表达验证
			valid = rule.test(value);
			if (!valid)
				message = '该 ' + name + ' 无效';
		}
	} else if (rule == 'require'){
		if (value === undefined || value === null || typeof value == 'string'){
			valid = false;
			message = '该 ' + name + ' 必须传入值';	
		}
	}
	return [valid, message];
};


// 输出对象
var jpart = {

	// 在数组 list 中查找 value
	inArray: function(value, list, fromIndex, key){
		if (!list || !list.length) return -1;
		if (fromIndex === undefined){
			fromIndex = 0;
		} else if (typeof fromIndex == "string"){	// 搜索对象
			key = fromIndex;
			fromIndex = 0;
		}
		if (key && typeof value == "object"){
			value = value[key];
			if (value === undefined)
				return -1;
		}
		for (var i=fromIndex; i<list.length; i++){
			var oValue = key ? list[i][key] : list[i];
			if (oValue == value)
				return i;
		}
		return -1;
	},
	// 提取数组 list 中属性为 key 的值列表
	pluck: function(list, key){
		var buffer = [];
		for (var i=0; i<list.length; i++){
			buffer.push(list[i][key]);
		}
		return buffer;
	},
	// 克隆对象
	clone: function(source, fields){
		if (!source) return source;
		if (typeof source != 'object') return source;
		var target;
		if (Array.isArray(source)){
			target = [];
			for (var item of source)
				target.push(item);
			return target;
		}
		target = {};
		if (fields){	// 只复制指定字段
			if (typeof fields == 'string')
				fields = fields.split(/\s*,\s*/);
			var findkey = false;
			for (var key of fields){
				var value = source[key];
				if (value !== undefined){
					target[key] = value;
					findkey = true;
				}
			}
			// 未复制任何字段，返回空
			if (!findkey)
				return null;
		} else {
			for (var key in source)
				target[key] = source[key];
		}
		return target;
	},
	
	// 生成随机数 length-随机数长度  radix-字符范围 [2,36]
	random: function(length, radix) {
		var AlphaMap = '0123456789abcdefghijklmnopqrstuvwxyz';
		if (!radix){
			radix = 10;		// 默认生成10进制数字
		} else if (typeof radix == 'string'){
			AlphaMap = radix;
			radix = AlphaMap.length;
		} else if (typeof radix == 'number'){
			if (radix < 2) radix = 2;
			if (radix > 36) radix = 36;
		} else {
			radix = 10;
		}
		var buffer = [];
		for (var i=0; i<length; i++) {
			var index = Math.floor(Math.random() * radix);
			buffer.push(AlphaMap[index]);
		}
		return buffer.join('');
	},
	// 生成时间戳
	timestamp: function(digits){
		var now = new Date();
		var text = now.getTime() + '';
		return digits ? text.substring(0, digits) : text;
	},
	// 随机生成12位交易号
	tradeno: function(){
		var now = (new Date()).getTime();
		var timeseq = now.toString(36).split('');
		var rand = 46656 + parseInt(Math.random()*1632960);	// 36^3=46656  36^4=1679616
		var randseq = rand.toString(36).split('');
		var rem = parseInt(randseq[0], 36) % 3;
		var confuses = [
			[0, 1, 2, 3, 4, 5, 6, 7],
			[2, 4, 1, 0, 6, 3, 5, 7],
			[3, 0, 4, 1, 5, 6, 2, 7],
		];
		// 打乱时间顺序
		var map = confuses[rem], list = [];
		for (var i=0; i<map.length; i++){
			if (i < 4)
				list.push(randseq[i]);
			list.push(timeseq[map[i]]);
		}
		return list.join('');
	},
	
	// 从数组创建 hashset, 使用 object 模拟
	hashset: function(array){
		var hash = {};
		for (var i=0; i<array.length; i++)
			hash[array[i]] = true;
		return hash;
	},

	// 转为整数、浮点数、布尔值、日期
	parseInt: function(value){
		if (!value) return 0;
		if (typeof value == 'number') return value;
		if (typeof value != 'string') return 0;
		var iValue = parseInt(value);
		return Number.isNaN(iValue) ? 0 : iValue;
	},
	parseFloat: function(value){
		if (!value) return 0;
		if (typeof value == 'number') return value;
		if (typeof value != 'string') return 0;
		var fValue = parseFloat(value);
		return Number.isNaN(fValue) ? 0 : fValue;
	},
	parseBoolean: function(value){
		if (!value) return false;
		if (typeof value == 'boolean') return value;
		if (typeof value == 'number') return value > 0;
		if (typeof value == 'string'){
			if (/^\d+$/.test(value))
				return parseInt(value) > 0;
			return value.toLowerCase() == 'true';
		}
		return false;
	},
	parseDate: function(value){
		if (!value) return null;
		if (value instanceof Date) return value;
		if (typeof value == 'number') return new Date(value);
		if (typeof value == 'string'){
			var aDate = value.split(/[- :TZ\/]/);	// 按减号、空格、冒号、斜线TZ拆分
			if (aDate.length < 1)
				return null;	// 日期格式错误
			if (!aDate[1])
				aDate[1] = 0;	// 1月
			else
				aDate[1] = parseInt(aDate[1],10) - 1;
			if (!aDate[2]) aDate[2] = 1;	// 1日
			if (!aDate[3]) aDate[3] = 0;	// 从0时开始计算
			if (!aDate[4]) aDate[4] = 0;	// 分钟
			if (!aDate[5]) aDate[5] = 0;	// 秒
			if (!aDate[6]) aDate[6] = 0;	// 毫秒
			return new Date(aDate[0], aDate[1], aDate[2], aDate[3], aDate[4], aDate[5], aDate[6]);
		}
		return null;
	},

	// 计算两个日期之间有多少天
	dayBetween: function(date1, date2){
		if (!date1)
			return 0;
		if (!date2)
			date2 = new Date();
		var diff = date2.getTime() - date1.getTime();
		if (diff < 0)
			return 0;
		return Math.ceil(diff/dayMillis);
	},
	
	// 计算两个日期的时间差，返回毫秒
	timeBetween: function(date1, date2){
		if (!date1)
			return 0;
		var time1 = this.parseDate(date1);
		var time2 = date2 ? this.parseDate(date2) : new Date();
		var delta = time1.getTime() - time2.getTime();
		return delta > 0 ? delta : -delta;
	},
	// 过滤指定html标签
	filterHtml: function(html, tags){
		if(typeof html != 'string')
			return html
		if (!html)
			return '';
		if (!tags)
			return html;
		var tags = tags.split(',');
		var reg = new RegExp('<(' + tags.join('|') + ')\\b[^>]*>', 'g');
		return html.replace(reg, '');
	},


	filterXSS: function(text){
		if (!text)
			return '';
		return text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
	},

	xssFilter: function(value){
		if (!value)
			return '';
		if (typeof value != 'string')
			return value;
		return value.replace(/</g, '&lt;').replace(/>/g, '&gt;');
	},
	// 过滤sql
	sqlFilter: function(value){
		if (!value)
			return '';
		if (typeof value != 'string')
			return value;
		return value.replace(/'/g, '\'\'');
	},
	// 过滤所有html标签
	htmlFilter: function(value){
		if (!value)
			return '';
		if (typeof value != 'string')
			return value;
		return value.replace(/<\D*>/g, '')
	},
	// 格式化日期
	defaultDateFormat: "yyyy-MM-dd hh:mm:ss",
	formatDate: function(value, fmt){
		if (!value) value = new Date();
		else if (typeof value.getMonth != 'function') return value;
		var obj = {
			"y+" : value.getFullYear(),		// 年份
			"M+" : value.getMonth()+1,		// 月份
			"d+" : value.getDate(),			// 日
			"h+" : value.getHours(),		// 小时
			"m+" : value.getMinutes(),		// 分
			"s+" : value.getSeconds(),		// 秒
			"q+" : Math.floor((value.getMonth()+3)/3),	// 季度
			"S+"  : value.getMilliseconds()	// 毫秒
		};
		if (!fmt) fmt = jpart.defaultDateFormat;
		for (var key in obj){
			var regex = new RegExp("("+ key +")");
			if (regex.test(fmt)){
				var text = obj[key] + '';
				if (key == 'y+'){
					text = text.substring(4 - RegExp.$1.length);
				} else {
					var zlen = RegExp.$1.length - text.length;
					if (zlen > 0)
						text = '0000'.substring(0, zlen) + text;
				}
				fmt = fmt.replace(RegExp.$1, text);
			}
		}
		return fmt;
	},
	// 格式化小数
	formatFloat: function(value, dot){
		if (!value)
			return 0;
		var num = Number(value);
		if (Number.isNaN(num))
			return 0;
		if (!dot)
			dot = 2;
		return num.toFixed(dot);
	},
	// 清除空字符串
	clearEmpty: function(bean){
		for (var key in bean){
			var value = bean[key];
			if (typeof value == 'string'){
				if (value.length > 0)
					value = value.trim();
				bean[key] = (value === '' || value === null || value === 'null') ? null : value;
			}
		}
		return bean;
	},
	// 系列化对象
	serialize: function(bean){
		var buffer = [];
		for (var key in bean){
			var value = bean[key];
			if (!value){
				buffer.push(key + "=");
			} else if (typeof value == "number" || typeof value == "boolean"){
				buffer.push(key + "=" + value);
			} else if (typeof value == "string"){
				//.push(key + "=" + encodeURIComponent(value));
				buffer.push(key + "=" + value);
			} else if (typeof value == "object"){
				if (value instanceof Date){	// 日期格式
					buffer.push(key + "=" + jpart.formatDate(value));
				} else if (Array.isArray(value)){
					// 数组按表单方式提交，不进行转码
					for (var i=0; i<value.length; i++){
						if (value[i])
							buffer.push(key + "=" + value[i]);
					}
				} else { // 转为 json 格式
					buffer.push(key + "=" + JSON.stringify(value));
				}
			}
		}
		return buffer.length > 0 ? buffer.join("&") : "";
	},
	// 连接Url参数
	urlcat: function(url, parameter, ignoreEmpty){
		if (!parameter) return url;
		var buffer = [];
		if (typeof parameter == "object"){
			for (var key in parameter){
				var value = parameter[key];
				if (value === undefined || value === ''){
					if (!ignoreEmpty)
						buffer.push(key + '=');
				} else if (typeof value == 'string'){
					buffer.push(key + '=' + encodeURIComponent(value));
				} else {
					buffer.push(key + '=' + value);
				}
			}
			if (buffer.length == 0)
				return url;
			parameter = buffer.join('&');
		}
		var symbal = url.indexOf('?') == -1 ? '?' : '&';
		return url + symbal + parameter;
	},
	// 下划线转驼峰命名法
	toCamelCase: function(name){
		if (!name)
			return name;
		if (typeof name == 'string')
			return _toCamelCase(name);
		// 转换数组
		if (Array.isArray(name)){
			for (var i=0; i<name.length; i++)
				name[i] = jpart.toCamelCase(name[i]);
			return name;
		}
		// 转换对象
		if (typeof name == 'object'){
			var map = {};
			for (var key in name){
				map[_toCamelCase(key)] = name[key];
			}
			return map;
		}
		return name;
	},
	// 驼峰转下划线命名风格
	toUnderScoreCase: function(name){
		if (!name)
			return name;
		if (typeof name == 'string')
			return _toUnderScoreCase(name);
		// 转换数组
		if (Array.isArray(name)){
			for (var i=0; i<name.length; i++)
				name[i] = jpart.toUnderScoreCase(name[i]);
			return name;
		}
		// 转换对象
		if (typeof name == 'object'){
			var map = {};
			for (var key in name){
				map[_toUnderScoreCase(key)] = name[key];
			}
			return map;
		}
		return name;
	},
	// 验证提交信息 require,number,date,max:x,min:x,/regex/
	verify: function(bean, rulesMap){
		this.clearEmpty(bean);
		for (var key in rulesMap){
			var rules = rulesMap[key];
			if (typeof rules == 'string')
				rules = rules.split(/\s*,\s*/);
			for (var i=0; i<rules.length; i++){
				bean[key] = this.filterHtml(bean[key],'script');
				var result = validate(bean, key, rules[i]);
				if (!result[0])
					return result;
			}
		}
		return [true, ''];
	},
	// 过滤 emoji 表情符号
	filterEmoji: function(text){
		return text ? text.replace(/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g, "") : text;
	},
	// 查询ip地址对应地区
	queryIp: function(ip){
		const axios = require("axios")
		return new Promise((resolve,reject)=>{
			axios({
				method:'get',
				url:'https://sp1.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php',
				params: {
					query: ip,
					resource_id: '5809',
					cb:'op_aladdin_callback',
					cb:'jQuery1102008716316532335622_1662260954901'
				},
				})
				.then(res=>{
					let zz = /(.*?\().*?/
					let string = res.data.match(zz)[0]
					let newsrting = res.data.replace(string,'')
					let obj = JSON.parse(newsrting.substring(0,newsrting.length-1))
					resolve(obj.data[0].location)
				})
				.catch(err=>{
					reject(err)
				})
		})
	}
	////////////////////////////////////////////////////////////////////////////
}

module.exports = jpart;
